﻿//-------------------------------------
// WBFSSync - WBFSSync.exe
//
// Copyright 2009 Caian (ÔmΣga Frøst) <frost.omega@hotmail.com> :
//
// WBFSSync is Licensed under the terms of the Microsoft Reciprocal License (Ms-RL)
//
// Library.cs:
//
// Gerencia pastas de jogos no computador e extrai informação sobre tipos de arquivo
//
//-------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using System.IO;

namespace WBFSSync
{
    enum LibraryFileType : byte
    {
        Unknown,
        Iso,
        Rar,
        Scrub,
    }

    class LibraryFile
    {
        public String Path = "";
        public String Name = "";
        public String Code = "";
        public Int32 Region = -1;
        public long FileSize = 0;
        public long WBFSSize = 0;
        public long ScrubFullSize = 0;
        public LibraryFileType Type = LibraryFileType.Unknown;
    }

    class LibraryFolder
    {
        public String Path = "";
        public FileSystemWatcher Watcher = null;
    }

    delegate void LibraryChangedDelegate();

    static class Library
    {
        public static List<LibraryFolder> Folders = new List<LibraryFolder>();
        public static List<LibraryFile> Files = new List<LibraryFile>();

        public static event LibraryChangedDelegate LibraryChanged;

        public static Dictionary<String, String> Extensions = new Dictionary<string, string>();

        //

        public static void AddFolder(String path)
        {
            if (path.Length == 0) return;
            path = Path.GetFullPath(path);

            if (IndexOfFolder(path) >= 0)
            {
                //Loga aviso

                return;
            }

            LibraryFolder folder = new LibraryFolder();

            folder.Path = path;
            folder.Watcher = new FileSystemWatcher(path);
            folder.Watcher.Changed += new FileSystemEventHandler(Watcher_Changed);
            folder.Watcher.Created += new FileSystemEventHandler(Watcher_Created);
            folder.Watcher.Deleted += new FileSystemEventHandler(Watcher_Deleted);
            folder.Watcher.Renamed += new RenamedEventHandler(Watcher_Renamed);
            folder.Watcher.EnableRaisingEvents = true;

            Folders.Add(folder);

            //
            String[] files = Directory.GetFiles(path, WiiDisc.typefilter_wiiiso);
            foreach (String file in files)
            {
                string filepath = Path.GetFullPath(file);
                LibraryFile libfile = new LibraryFile();
                if (WiiDisc.GetIsoInformation(filepath, out libfile.Code, out libfile.Name, out libfile.Region, 
                    out libfile.WBFSSize) == 0)
                {
                    if (IndexOfFileByCode(libfile.Code) >= 0)
                    {
                        //Loga aviso

                        libfile = null;
                        continue;
                    }

                    libfile.ScrubFullSize = 0;
                    libfile.FileSize = (new FileInfo(filepath).Length);
                    libfile.WBFSSize *= WBFSDevice.wiiSectorSize;

                    libfile.Path = filepath;
                    libfile.Type = LibraryFileType.Iso;

                    Files.Add(libfile);
                }
            }

            files = Directory.GetFiles(path, ScrubDisc.typefilter_wiiscrub);
            foreach (String file in files)
            {
                string filepath = Path.GetFullPath(file);
                LibraryFile libfile = new LibraryFile();
                long fullsize = 0;
                if (ScrubDisc.GetScrubbedInfo(filepath, out libfile.Code, out libfile.Name, out libfile.Region, out fullsize,
                    out libfile.WBFSSize)
                    == 0)
                {
                    if (IndexOfFileByCode(libfile.Code) >= 0)
                    {
                        //Loga aviso

                        libfile = null;
                        continue;
                    }

                    libfile.ScrubFullSize = fullsize;
                    libfile.FileSize = (new FileInfo(filepath).Length);
                    libfile.WBFSSize *= WBFSDevice.wiiSectorSize;

                    libfile.Path = filepath;
                    libfile.Type = LibraryFileType.Scrub;

                    Files.Add(libfile);
                }
            }

            OnLibraryChanged();
        }

        public static void RemoveFolder(string path, bool quiet)
        {
            if (path.Length == 0) return;
            path = Path.GetFullPath(path);

            int folder = IndexOfFolder(path);
            if (folder < 0)
            {
                //Loga aviso
                Log.LogLine(Language.log_directory_not_found, LogMessageType.Error, path);

                return;
            }

            Folders[folder].Watcher.Dispose();
            Folders[folder].Watcher = null;
            Folders.RemoveAt(folder);

            for (int i = Files.Count - 1; i >= 0; i--)
            {
                if (String.Compare(Path.GetDirectoryName(Files[i].Path), path, true) == 0)
                    Files.RemoveAt(i);
            }

            if (!quiet) OnLibraryChanged();
        }

        public static void RemoveFolders(string[] folders)
        {
            for (int k = 0; k < folders.Length; k++)
            {
                string path = Path.GetFullPath(folders[k]);
                if (path.Length == 0) return;
                int folder = IndexOfFolder(path);
                if (folder < 0)
                {
                    //Loga aviso
                    Log.LogLine(Language.log_directory_not_found, LogMessageType.Error, path);

                    return;
                }

                Folders[folder].Watcher.Dispose();
                Folders[folder].Watcher = null;
                Folders.RemoveAt(folder);

                for (int i = Files.Count - 1; i >= 0; i--)
                {
                    if (String.Compare(Path.GetDirectoryName(Files[i].Path), path, true) == 0)
                        Files.RemoveAt(i);
                }
            }

            OnLibraryChanged();
        }

        public static void Clear()
        {
            while (Folders.Count > 0)
            {
                for (int i = Files.Count - 1; i >= 0; i--)
                {
                    if (String.Compare(Path.GetDirectoryName(Files[i].Path), Folders[0].Path, true) == 0)
                        Files.RemoveAt(i);
                }

                Folders[0].Watcher.Dispose();
                Folders[0].Watcher = null;
                Folders.RemoveAt(0);

            }

            OnLibraryChanged();
        }

        static void OnLibraryChanged()
        {
            if (LibraryChanged != null) LibraryChanged();
        }

        static void Watcher_Renamed(object sender, RenamedEventArgs e)
        {
            int i = IndexOfFileByPath(e.OldFullPath);
            if (i >= 0)
            {
                Files[i].Path = e.FullPath;
            }
        }

        static void Watcher_Deleted(object sender, FileSystemEventArgs e)
        {
            int i = IndexOfFileByPath(e.FullPath);
            if (i >= 0)
            {
                Files.RemoveAt(i);
            }
        }

        static void Watcher_Created(object sender, FileSystemEventArgs e)
        {
            //Previne mensagens e mais mensagens de erro no Log por não conseguir travar o Contexto
            if (IOManager.ContainsHandle(e.FullPath)) return;

            LibraryFile libfile;

            switch (Path.GetExtension(e.FullPath))
            {
                case WiiDisc.ext_wiiiso:

                    libfile = new LibraryFile();
                    if (WiiDisc.GetIsoInformation(e.FullPath, out libfile.Code, out libfile.Name, out libfile.Region,
                        out libfile.WBFSSize) == 0)
                    {
                        libfile.ScrubFullSize = 0;
                        libfile.FileSize = (new FileInfo(e.FullPath).Length);
                        libfile.WBFSSize *= WBFSDevice.wiiSectorSize;

                        libfile.Path = e.FullPath;
                        libfile.Type = LibraryFileType.Iso;

                        Files.Add(libfile);
                    }

                    break;

                case ScrubDisc.ext_wiiscrub:

                    libfile = new LibraryFile();
                    long fullsize = 0;
                    if (ScrubDisc.GetScrubbedInfo(e.FullPath, out libfile.Code, out libfile.Name, out libfile.Region, 
                        out fullsize, out libfile.WBFSSize)
                        == 0)
                    {
                        libfile.ScrubFullSize = fullsize;
                        libfile.FileSize = (new FileInfo(e.FullPath).Length);
                        libfile.WBFSSize *= WBFSDevice.wiiSectorSize;

                        libfile.Path = e.FullPath;
                        libfile.Type = LibraryFileType.Scrub;

                        Files.Add(libfile);
                    }
                    
                    break;
            }
        }

        static void Watcher_Changed(object sender, FileSystemEventArgs e)
        {
            //Previne mensagens e mais mensagens de erro no Log por não conseguir travar o Contexto
            if (IOManager.ContainsHandle(e.FullPath)) return;

            int i = IndexOfFileByPath(e.FullPath);
            if (i >= 0)
            {
                try
                {
                    switch (Files[i].Type)
                    {
                        case LibraryFileType.Iso:
                            if (WiiDisc.GetIsoInformation(e.FullPath, out Files[i].Code, out Files[i].Name, out Files[i].Region,
                            out Files[i].WBFSSize) == 0)
                            {
                                Files[i].ScrubFullSize = 0;
                                Files[i].FileSize = (new FileInfo(e.FullPath).Length);
                                Files[i].WBFSSize *= WBFSDevice.wiiSectorSize;
                            }
                            break;
                        case LibraryFileType.Scrub:
                            if (ScrubDisc.GetScrubbedInfo(e.FullPath, out Files[i].Code, out Files[i].Name, out Files[i].Region,
                                out Files[i].ScrubFullSize, out Files[i].WBFSSize) == 0)
                            {
                                Files[i].FileSize = (new FileInfo(e.FullPath).Length);
                                Files[i].WBFSSize *= WBFSDevice.wiiSectorSize;
                            }
                            break;
                        case LibraryFileType.Rar:
                            break;
                    }
                }
                catch (Exception ex)
                {
                    Log.LogException(ex);
                }
            }
        }

        public static int IndexOfFolder(string path)
        {
            for (int i = 0; i < Folders.Count; i++)
                if (String.Compare(Folders[i].Path, path, true) == 0) return i;

            return -1;
        }

        public static int IndexOfFileByPath(string path)
        {
            for (int i = 0; i < Files.Count; i++)
                if (String.Compare(Files[i].Path, path, true) == 0) return i;

            return -1;
        }

        public static int IndexOfFileByCode(string code)
        {
            for (int i = 0; i < Files.Count; i++)
                if (String.Compare(Files[i].Code, code, true) == 0) return i;

            return -1;
        }

        public static int IndexOfFileByName(string name)
        {
            for (int i = 0; i < Files.Count; i++)
                if (String.Compare(Files[i].Name, name, true) == 0) return i;

            return -1;
        }

        public static string GetExtensionDesc(String ext)
        {
            ext = ext.ToLower();
            if (Extensions.ContainsKey(ext)) return Extensions[ext];

            RegistryKey extKey = Registry.ClassesRoot.OpenSubKey(ext, RegistryKeyPermissionCheck.ReadSubTree);
            if (extKey == null)
                return Language.explorer_file + ext.ToUpper();

            //-------------------

            Object extdefault = extKey.GetValue("");
            extKey.Close();
            if ((extdefault == null) || !(extdefault is String))
                return Language.explorer_file + ext.ToUpper();

            //-------------------

            RegistryKey extdescKey = Registry.ClassesRoot.OpenSubKey(extdefault as String, RegistryKeyPermissionCheck.ReadSubTree);
            if (extdescKey == null)
                return Language.explorer_file + ext.ToUpper();

            //-------------------

            String extinfo = "";

            Object extdesc = extdescKey.GetValue("");
            if ((extdesc != null) && (extdesc is String))
            {
                extinfo = extdesc as String;
            }
            else
            {
                return Language.explorer_file + ext.ToUpper();
            }

            //-------------------

            Extensions.Add(ext, extinfo);
            return extinfo;
        }

        public static bool ValidateFolders()
        {
            bool result = true;

            for (int i = Folders.Count - 1; i >= 0; i--)
            {
                if (!Directory.Exists(Library.Folders[i].Path))
                {
                    RemoveFolder(Library.Folders[i].Path, true);
                    result = false;
                }
            }

            return result;
        }

        public static void ValidateFiles()
        {
            for (int i = Files.Count - 1; i >= 0; i--)
            {
                if (!File.Exists(Library.Files[i].Path))
                    Files.RemoveAt(i);
            }
        }
    }
}
